package com.boe.hbd.client.activity;

import java.util.ArrayList;
import java.util.List;

import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.AnimationDrawable;
import android.location.Location;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;

import com.boe.hbd.client.Constants;
import com.boe.hbd.client.R;
import com.boe.hbd.client.contextmenu.IconContextMenu.IconContextItemSelectedListener;
import com.boe.hbd.client.domain.AttachedOverlayItem;
import com.boe.hbd.client.domain.Landscape;
import com.boe.hbd.client.overlaymanager.ManagedOverlay;
import com.boe.hbd.client.overlaymanager.ManagedOverlayGestureDetector.OnOverlayGestureListener;
import com.boe.hbd.client.overlaymanager.ManagedOverlayItem;
import com.boe.hbd.client.overlaymanager.OverlayManager;
import com.boe.hbd.client.overlaymanager.TitleMarkerRenderer;
import com.boe.hbd.client.overlaymanager.ZoomEvent;
import com.boe.hbd.client.overlaymanager.lazyload.LazyLoadCallback;
import com.boe.hbd.client.overlaymanager.lazyload.LazyLoadException;
import com.boe.hbd.client.persistence.EntityManager;
import com.boe.hbd.client.service.LandscapeService;
import com.boe.hbd.client.service.LocationService;
import com.boe.hbd.client.service.ServiceManager;
import com.boe.hbd.client.service.TextToSpeechService;
import com.boe.hbd.client.utils.GpsPoint;
import com.boe.hbd.client.utils.Utils;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;

public class MainActivity extends MapActivity implements OnOverlayGestureListener, LazyLoadCallback, Runnable, OnClickListener, IconContextItemSelectedListener {

	private static final String TAG = "MainActivity";
	private static final int MENU_BACK    = 1;
	private static final int MENU_PREFS   = 2;
	private static final int MENU_LICENSE = 3;
	private static final int MENU_EXIT    = 4;
	
	private MapView mapView;
	private MapController mapController;
	private ManagedOverlay nearbyLandscapesOverlay;
	private OverlayManager overlayManager;
	private boolean loaderInitialized = false ;
	private Handler uiExecutor = null;
	private Thread userEventListener = null;
	private GpsPoint currentGpsPoint;
	private LocationService locationService;
	private LandscapeService landscapeService;
	private EntityManager entityManager;	
	private TextToSpeechService tts;
	
	@Override
	public void onCreate(Bundle theInstance) {
		uiExecutor = new Handler();
		userEventListener = new Thread(this);
		locationService = ServiceManager.getInstance(this).getService(LocationService.class);
		landscapeService = ServiceManager.getInstance(null).getService(LandscapeService.class);
		entityManager = ServiceManager.getInstance(null).getService(EntityManager.class);
		tts = ServiceManager.getInstance(null).getService(TextToSpeechService.class);
		// STEP 1: initialize the layout
		ServiceManager.getInstance(this);
		super.onCreate(theInstance);

		boolean productionEnabled = ServiceManager.getInstance(null).isProductionEnabled();
		if (productionEnabled) {
			setContentView(R.layout.main_prod);
		} else {
			setContentView(R.layout.main_debug);
		}
		Log.i(getClass().getSimpleName() , productionEnabled ? "prodMapView" : "debugMapView" ); 
		
		mapView = (MapView) findViewById(R.id.mapView);
		mapView.setVisibility(View.VISIBLE);
		mapController = mapView.getController();
		mapView.setSatellite(false);
		mapController.setZoom(16);
		mapView.setBuiltInZoomControls(true);
		// STEP 2: register the listen event
		findViewById(R.id.satelliteOrMapImageButton).setOnClickListener(this);
		findViewById(R.id.iamhereImageButton).setOnClickListener(this);
		findViewById(R.id.searchImageButton).setOnClickListener(this);
		// STEP 3: create the overlays
		overlayManager = new OverlayManager(this, mapView);
		nearbyLandscapesOverlay = overlayManager.createOverlay("nearbyLandscapesOverlay");
		nearbyLandscapesOverlay.setOnOverlayGestureListener(this);
		nearbyLandscapesOverlay.setCustomMarkerRenderer(new TitleMarkerRenderer(8, 16, 14));
		nearbyLandscapesOverlay.enableLazyLoadAnimation((ImageView) findViewById(R.id.loaderImageButton)).setAnimationDrawable((AnimationDrawable) getResources().getDrawable(R.drawable.loader));
		nearbyLandscapesOverlay.setLazyLoadCallback(this);
		// STEP 4: register the icon buttons click events
		userEventListener.start(); 		
		// ServiceManager.getInstance(null).getService(SensorService.class);
	}

	/**
	 * this method will keep watching the user's event
	 */
	public void run() {
		while (true) {
			// STEP 1: process the initialize loader animation logic
			if (!loaderInitialized) { 
				loaderInitialized = true ;
				uiExecutor.postDelayed(new Runnable(){
					public void run() {
						((AnimationDrawable)findViewById(R.id.loaderImageButton).getBackground()).start();
					}
				},1000);
			}
			// STEP 2: query current location
			if ( currentGpsPoint == null ) {
				Location location =  locationService.getLocation();
				if (location != null) {
					currentGpsPoint = new GpsPoint(location.getLatitude(),location.getLongitude());
					mapController.animateTo(currentGpsPoint.toGeoPoint());
					mapController.setCenter(currentGpsPoint.toGeoPoint());
					if (loaderInitialized) {
						uiExecutor.post(new Runnable(){
							public void run() {
								((AnimationDrawable)findViewById(R.id.loaderImageButton).getBackground()).stop();
								findViewById(R.id.loaderImageButton).setVisibility(View.INVISIBLE);
							}
						});
					}
					// STEP 3: locate to currentGpsPoint
					overlayManager.populate(); 
					nearbyLandscapesOverlay.invokeLazyLoad(1000);
				}
			}
			try {
				Thread.sleep(500);
			} catch (Throwable e) {
				Log.e(TAG, e.getMessage());
			}
		}
	}
	
	/**
	 * this method process the map control buttons click event
	 */
	public void onClick(View v) {

		if (speakEnabled()) {
			soundPlay(R.raw.up6);
		}
		
		if (v.getId() == R.id.satelliteOrMapImageButton) {
			if (mapView.isSatellite()) {
				mapView.setSatellite(false);
				((ImageView)v).setImageResource(R.drawable.satellite);
			} else {
				mapView.setSatellite(true);
				((ImageView)v).setImageResource(R.drawable.map);
			}
		} else if (v.getId() == R.id.iamhereImageButton) {
			Location location =  locationService.getLocation();
			if (location != null) {
				currentGpsPoint = new GpsPoint(location.getLatitude(),location.getLongitude());
				mapController.animateTo(currentGpsPoint.toGeoPoint());
				mapController.setCenter(currentGpsPoint.toGeoPoint());
				nearbyLandscapesOverlay.invokeLazyLoad(1000);
			}
		} else if (v.getId() == R.id.searchImageButton) {
			Intent intent = new Intent();
			intent.setClass(this, LandscapeListActivity.class);
			Bundle bundle = new Bundle();
			if (currentGpsPoint != null) {
			    bundle.putDouble(Constants.KEY_LATITUDE, currentGpsPoint.getLatitude());
			    bundle.putDouble(Constants.KEY_LONGTITUDE, currentGpsPoint.getLongitude());
			}
		    bundle.putInt(Constants.ACTIVITY_REQUEST_CODE, Constants.ACTIVITY_REQUEST_CODE_LIST_LANDSCAPE);
		    intent.putExtras(bundle);
			startActivityForResult(intent,Constants.ACTIVITY_REQUEST_CODE_LIST_LANDSCAPE);
		}
	}
	
    //
	// User Action Callback
	//
	
	public void onIconContextItemSelected(MenuItem item, Object data) {
		Landscape landscape = null ;
		if (data != null) {
			landscape = (Landscape)data;
		}
		if (item.getItemId() == R.id.addLandscape) {
			Intent intent = new Intent(this,LandscapeIntroductionActivity.class);
		    Bundle bundle = new Bundle();
		    bundle.putDouble(Constants.KEY_LATITUDE, currentGpsPoint.getLatitude());
		    bundle.putDouble(Constants.KEY_LONGTITUDE, currentGpsPoint.getLongitude());
		    bundle.putInt(Constants.ACTIVITY_REQUEST_CODE, Constants.ACTIVITY_REQUEST_CODE_ADD_LANDSCAPE);
		    intent.putExtras(bundle);
		    startActivityForResult(intent,Constants.ACTIVITY_REQUEST_CODE_ADD_LANDSCAPE);
		} else if (item.getItemId() == R.id.deleteLandscape) {
			if (landscape != null) {
				entityManager.remove(landscape);
				nearbyLandscapesOverlay.invokeLazyLoad(1000);
			}
		} else if (landscape != null) {
			Long landscapeId = landscape.getId();
			int cmdId = item.getItemId();
			LandscapeHelper.processRequestForLandscape(this,landscapeId, cmdId);
		}
	}

	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (resultCode == RESULT_OK) {
			if (requestCode == Constants.ACTIVITY_REQUEST_CODE_ADD_LANDSCAPE 
					|| requestCode == Constants.ACTIVITY_REQUEST_CODE_VIEW_LANDSCAPE_DESCRIPTION  ) {
				Long landscapeId = data.getExtras().getLong(Constants.KEY_LANDSCAPE_ID);
				if (landscapeId != null) {
					nearbyLandscapesOverlay.invokeLazyLoad(1000);
				}
			} else if ( requestCode == Constants.ACTIVITY_REQUEST_CODE_CAPTURE_IMAGE 
			        || requestCode == Constants.ACTIVITY_REQUEST_CODE_CAPTURE_VOICE
			        || requestCode == Constants.ACTIVITY_REQUEST_CODE_CAPTURE_VIDEO ) {
				Long landscapeId = null ;
				Landscape landscape = getCurrentLandscape();
				if (landscape != null) {
					landscapeId = landscape.getId();
				}
				LandscapeHelper.processResultForLandscape(this,requestCode, data, landscapeId);
			}
		}
	}

	//
	// Map Overlay Functions
	//
	
	public List<ManagedOverlayItem> lazyload(GeoPoint topLeft, GeoPoint bottomRight, ManagedOverlay overlay) throws LazyLoadException {
		GpsPoint here = currentGpsPoint; 
		List<Landscape> landscapes = landscapeService.findLandscapesByArea(new GpsPoint(topLeft), new GpsPoint(bottomRight),here,null,null,null);
		List<ManagedOverlayItem> landscapeItems = new ArrayList<ManagedOverlayItem>();
		if (landscapes != null) {
			for (Landscape landscape : landscapes) {
				AttachedOverlayItem<Landscape> item = new AttachedOverlayItem<Landscape>(landscape, landscape.getGeoPoint(), landscape.getName(), "");
				if (here != null && here.equalsGeoPoint( landscape.getGeoPoint() ) ) {
					item.setIcon(getResources().getDrawable(R.drawable.iamhere_small)).setSelected(true);
					here = null ;
				} else {
					item.setIcon(getResources().getDrawable(R.drawable.marker));
				}
				landscapeItems.add(item);
			}
		}
		if (here != null) {
			AttachedOverlayItem<Landscape> item = new AttachedOverlayItem<Landscape>(null, here.toGeoPoint(), getResources().getString(R.string.iamhere), "");
			item.setIcon(getResources().getDrawable(R.drawable.iamhere_small));
			landscapeItems.add(item);
		}
		return landscapeItems;
	}

	@SuppressWarnings("unchecked")
    public void onLongPressFinished(MotionEvent e, ManagedOverlay overlay, GeoPoint point, ManagedOverlayItem item) {
		int menuId = -1 ;
		Landscape landscape = null ;
		if (item instanceof AttachedOverlayItem) {
			landscape = ((AttachedOverlayItem<Landscape>)item).getAttachedObj();
			if (landscape != null && landscape.getLatitude() != null && landscape.getLongtitude() != null) {
				point = landscape.getGeoPoint();
				menuId= R.menu.main_edit_existing_landscape;
				if (speakEnabled()) {
					tts.say(getResources().getString(R.string.edit)+landscape.getName());
				}
			} else if ( item.getPoint() != null ) {
				point = item.getPoint();
			}
		}
		if (point != null) {
			currentGpsPoint = new GpsPoint(point) ;
		}
		if (menuId >= 0) {
			LandscapeHelper.buildContextMenu(this, this, landscape, menuId);
		}
	}

	@SuppressWarnings("unchecked")
    public boolean onSingleTap(MotionEvent e, ManagedOverlay overlay, GeoPoint point, ManagedOverlayItem item) {
		int menuId = -1 ;
		Landscape landscape = null ;
		if (item instanceof AttachedOverlayItem) {
			landscape = ((AttachedOverlayItem<Landscape>)item).getAttachedObj();
			// when we clicked on an existed point, need fetch the location information from it
			if (landscape != null && landscape.getLatitude() != null && landscape.getLongtitude() != null) {
				point = landscape.getGeoPoint();
				menuId= R.menu.main_view_existing_landscape;
				if (speakEnabled()) {
				    tts.say(landscape.getDescription());
				}
			}
			// clicked i am here icon
			else {
				point = item.getPoint() == null ? point : item.getPoint();
				menuId= R.menu.main_current_location;
			}
		}
		// clicked on empty position
		else {
		}
		boolean refresh = true ;
		if (currentGpsPoint != null && currentGpsPoint.equalsGeoPoint( point )) {
			refresh = false;
		}
		currentGpsPoint = new GpsPoint(point) ;
		if (refresh) {
			nearbyLandscapesOverlay.invokeLazyLoad(1000);
		}
		if (menuId >= 0) {
			LandscapeHelper.buildContextMenu(this, this, landscape, menuId);
		}
		return true;
	}
		
	public boolean onZoom(ZoomEvent zoom, ManagedOverlay overlay) {
		return false;
	}

	public boolean onDoubleTap(MotionEvent e, ManagedOverlay overlay,
			GeoPoint point, ManagedOverlayItem item) {
		return false;
	}

	public void onLongPress(MotionEvent e, ManagedOverlay overlay) {
	}

	public boolean onScrolled(MotionEvent e1, MotionEvent e2, float distanceX,float distanceY, ManagedOverlay overlay) {
		return false;
	}

	//
	// Life Cycle Functions
	//
	@Override
	public void onStart() {
		super.onStart();
		ServiceManager.getInstance(null).getService(LocationService.class).watchLocationUpdates();
	}

	@Override
	public void onStop() {
		super.onStop();
		ServiceManager.getInstance(null).getService(LocationService.class).stopWatchLocationUpdates();
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		ServiceManager.getInstance(null).destroy();
	}

    public void onPause(){ 
    	super.onPause();
    	ServiceManager.getInstance(null).pause();
    }
    
    public void onResume(){
		super.onResume();
		ServiceManager.getInstance(null).resume();
    }
    
	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}	
	

	//TBD: work in background
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
			moveTaskToBack(true);
			return true;
		} 
		return super.onKeyDown(keyCode, event);
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		menu.add(0, MENU_BACK, Menu.NONE, getString(R.string.menu_send_to_back)).setIcon(android.R.drawable.ic_menu_set_as);
		menu.add(1, MENU_PREFS, Menu.NONE, getString(R.string.preferences)).setIcon(android.R.drawable.ic_menu_preferences);
		menu.add(2, MENU_LICENSE, Menu.NONE, getString(R.string.license)).setIcon(android.R.drawable.ic_menu_view);
		menu.add(3, MENU_EXIT, Menu.NONE,getString(R.string.menu_exit)).setIcon(android.R.drawable.ic_menu_close_clear_cancel);
		return true;
	}
	
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case MENU_BACK:
			moveTaskToBack(true);
			break;
		case MENU_PREFS:
			startActivity(new Intent(this, EditPreferences.class));
			break;
		case MENU_LICENSE:
			Utils.textDialog(this, getString(R.string.license), getString(R.string.license_content));
			break;
		case MENU_EXIT:
			((NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();
		    System.exit(0);
			break;
		}
		return (super.onOptionsItemSelected(item));
	}
	
	//
	// Utilities Methods
	//
	private void soundPlay(final int sound) {
		uiExecutor.post(new Runnable() {
			public void run() {
				MediaPlayer mediaPlayer = MediaPlayer.create(MainActivity.this, sound);
				mediaPlayer.start();
				while (mediaPlayer.isPlaying()) {
					Thread.yield();
				}
				mediaPlayer.release();
			}
		});
	}

	private boolean speakEnabled() {
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
		return prefs.getString("speakEnabled", "enabled").equals("enabled");
	}
	
	@SuppressWarnings("unchecked")
    private Landscape getCurrentLandscape(){  
		for ( ManagedOverlayItem item : nearbyLandscapesOverlay.getOverlayItems() ) {
			if ( item.isSelected() && ( item instanceof AttachedOverlayItem) ) {
				return ((AttachedOverlayItem<Landscape>)item).getAttachedObj() ;
			}
		} 
		return null ;
	}
}
